// Copyright 2001 freehep
package org.freehep.util.io;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Decrypts using the EEXEC form (Used by Type 1 fonts).
 * 
 * @author Simon Fischer
 * @version $Id: EEXECDecryption.java,v 1.3 2008/05/04 12:22:11 murkle Exp $
 */
public class EEXECDecryption extends InputStream implements EEXECConstants {

    private int n, c1, c2, r;

    private InputStream in;

    private boolean first = true;

    /**
     * Creates an EEXECDecryption from the given stream
     * 
     * @param in stream to read from
     */
    public EEXECDecryption(InputStream in) {
        this(in, EEXEC_R, N);
    }

    /**
     * Creates an EEXECDecryption from the given stream
     * 
     * @param in stream to read from
     * @param r
     * @param n
     */
    public EEXECDecryption(InputStream in, int r, int n) {
        this.in = in;
        this.r = r;
        this.n = n;
        this.c1 = C1;
        this.c2 = C2;
    }

    private int decrypt(int cipher) {
        int plain = (cipher ^ (r >>> 8)) % 256;
        r = ((cipher + r) * c1 + c2) % 65536;
        return plain;
    }

    public int read() throws IOException {
        if (first) {
            byte[] bytes = new byte[n];
            boolean notHex = false;
            for (int i = 0; i < bytes.length; i++) {
                int c = in.read();
                bytes[i] = (byte) c;
                if (!Character.isDigit((char) c) && !((c >= 'a') && (c <= 'f'))
                        && !((c >= 'A') && (c <= 'F')))
                    notHex = true;
            }
            if (notHex) {
                for (int i = 0; i < bytes.length; i++) {
                    decrypt(bytes[i] & 0x00ff);
                }
            } else {
                InputStream tempIn = new ASCIIHexInputStream(
                        new ByteArrayInputStream(bytes), true);
                int asciiDecoded;
                int byteCount = 0;
                while ((asciiDecoded = tempIn.read()) >= 0) {
                    decrypt(asciiDecoded);
                    byteCount++;
                }
                in = new ASCIIHexInputStream(in, true);
                while (byteCount < n) {
                    decrypt(in.read());
                    byteCount++;
                }

            }
            first = false;
        }

        int b = in.read();
        if (b == -1)
            return -1;
        else
            return decrypt(b);
    }

    public void close() throws IOException {
        super.close();
        in.close();
    }
}
